import { error, JsonAsset, SceneAsset, Asset, AssetManager, Node } from "cc";
import SUI, { InitedPrefabCallback, UIWindowOption } from "../ui/SUI";
import ResLoader, { AssetBundle, AssetType, BundleLoadOption, JsonAssetSource, RemoteLoadOption } from "../resource/ResLoader";

type SceneLoadOption = { [key: string]: any; preset?: "string"; } | null;

declare module "cc" {
    namespace AssetManager {
        interface Bundle {
            loadSync<T extends Asset>(paths: string | string[], type?: AssetType<T>): Promise<T | T[]>;
            loadDirSync<T extends Asset>(dir: string, type?: AssetType<T> | null): Promise<T[]>;
            loadSceneSync(sceneName: string, options: SceneLoadOption): Promise<SceneAsset>;
            // 加载json
            loadJson(path: string, onComplete: (err: Error, data: JsonAssetSource) => void): void;
            loadJsonSync(path: string): Promise<JsonAssetSource>;

            initPrefab(assetPath: string, parent?: Node): Node;
            initPrefabSafe(assetPath: string, parent?: Node, onComplete?: InitedPrefabCallback): void;
            initPrefabSync(assetPath: string, parent?: Node): Promise<Node>;

            initWindow(assetPath: string, parent?: Node, options?: UIWindowOption): Node;
            initWindowSafe(assetPath: string, parent?: Node | UIWindowOption | InitedPrefabCallback, options?: UIWindowOption | InitedPrefabCallback, onComplete?: InitedPrefabCallback): void;
            initWindowSync(assetPath: string, parent?: Node, options?: UIWindowOption): Promise<Node>;
        }
    }

    interface AssetManager {
        loadSync(paths: string | string[], options: BundleLoadOption): Promise<AssetBundle>;
        loadRemoteSync<T extends Asset>(url: string, options: RemoteLoadOption): Promise<T>;
        loadRemoteJson(jsonUrl: string, onComplete: (err: Error, data: JsonAssetSource) => void): void;
        loadRemoteJsonSync(jsonUrl: string): Promise<JsonAssetSource>;
    }
}

export default class AssetManagerExt {
    public static Init() {
        if (AssetManager.prototype["$__sgameExtension__"]) {
            return;
        }

        //@ts-ignore
        AssetManager.prototype["$__sgameExtension__"] = true;
        this._InitAssetManagerExt();
        this._InitAssetBundleExt();
    }

    private static _InitAssetManagerExt() {
        AssetManager.prototype.loadSync = function (nameOrUrl: string, options: BundleLoadOption): Promise<AssetBundle> {
            return ResLoader.LoadBundleSync(nameOrUrl, options);
        }

        AssetManager.prototype.loadRemoteSync = function <T extends Asset>(url: string, options: RemoteLoadOption = null): Promise<T> {
            return ResLoader.LoadRemoteSync(url, options);
        }

        AssetManager.prototype.loadRemoteJson = function (jsonUrl: string, onComplete: (err: Error, data: JsonAssetSource) => void) {
            ResLoader.LoadRemoteJson(jsonUrl, onComplete);
        }

        AssetManager.prototype.loadRemoteJsonSync = function (jsonUrl: string): Promise<JsonAssetSource> {
            return ResLoader.LoadRemoteJsonSync(jsonUrl);
        }
    }

    private static _InitAssetBundleExt() {
        AssetManager.Bundle.prototype.loadSync = function <T extends Asset>(paths: string | string[], type?: AssetType<T>): Promise<any> {
            return new Promise((resolve, reject) => {
                this.load(paths, type, (err: Error, data) => {
                    if (err) {
                        error(err.message);
                    }

                    resolve(data);
                });
            });
        }

        AssetManager.Bundle.prototype.loadDirSync = function <T extends Asset>(path: string, type?: AssetType<T>): Promise<T[]> {
            return new Promise((resolve, reject) => {
                this.loadDir(path, type, (err: Error, data: T[]) => {
                    if (err) {
                        error(err.message);
                    }

                    resolve(data);
                });
            });
        }

        AssetManager.Bundle.prototype.loadSceneSync = function (sceneName: string, options: SceneLoadOption): Promise<SceneAsset> {
            return new Promise((resolve, reject) => {
                this.loadScene(sceneName, options, (err: Error, data: SceneAsset) => {
                    if (err) {
                        error(err.message);
                    }

                    resolve(data);
                });
            });
        }

        AssetManager.Bundle.prototype.loadJson = function (path: string, onComplete: (err: Error, data: JsonAssetSource) => void): void {
            this.load(path, JsonAsset, (err: Error, json: JsonAsset) => {
                onComplete(err, json != null ? json.json : null);
            });
        }

        // 加载json
        AssetManager.Bundle.prototype.loadJsonSync = function (path: string): Promise<JsonAssetSource> {
            return new Promise((resolve, reject) => {
                this.loadJson(path, (err: Error, data: JsonAssetSource) => {
                    if (err) {
                        return reject(err);
                    }

                    resolve(data);
                })
            })
        }

        AssetManager.Bundle.prototype.initPrefab = function (path: string, parent?: Node): Node {
            let assetPath: string = `${this.name}/${path}`;
            return SUI.InitPrefab(assetPath, parent);
        }

        AssetManager.Bundle.prototype.initPrefabSafe = function (path: string, parent?: Node, onComplete?: InitedPrefabCallback): void {
            let assetPath: string = `${this.name}/${path}`;
            SUI.InitPrefabSafe(assetPath, parent, onComplete);
        }

        AssetManager.Bundle.prototype.initPrefabSync = function (path: string, parent?: Node): Promise<Node> {
            let assetPath: string = `${this.name}/${path}`;
            return SUI.InitPrefabSync(assetPath, parent);
        }

        AssetManager.Bundle.prototype.initWindow = function (path: string, parent?: Node, options?: UIWindowOption): Node {
            let assetPath: string = `${this.name}/${path}`;
            return SUI.InitWindow(assetPath, parent, options);
        }

        AssetManager.Bundle.prototype.initWindowSafe = function (path: string, parent?, options?, onComplete?): void {
            let assetPath: string = `${this.name}/${path}`;
            SUI.InitWindowSafe(assetPath, parent as Node, options as UIWindowOption, onComplete);
        }

        AssetManager.Bundle.prototype.initWindowSync = function (path: string, parent?: Node, options?: UIWindowOption): Promise<Node> {
            let assetPath: string = `${this.name}/${path}`;
            return SUI.InitWindowSync(assetPath, parent, options);
        }
    }
}